package com.shortlink.admin.base;

import cn.hutool.core.lang.hash.MurmurHash;
import cn.hutool.core.util.ObjectUtil;
import com.shortlink.admin.config.AdminConfiguration;
import com.shortlink.admin.dao.entity.DomainDO;
import com.shortlink.admin.dao.entity.ShortLinkDO;
import com.shortlink.admin.service.ShortLinkService;
import com.shortlink.common.exception.ServiceException;
import com.shortlink.common.user.UserContext;
import com.shortlink.common.util.Base62Util;
import com.shortlink.common.util.RBloomFilterUtils;
import jakarta.annotation.Resource;
import org.redisson.api.RBloomFilter;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;

import static com.shortlink.common.constant.RedisKeyConstant.DOMAIN_SHORT_URI_BLOOM_KEY;
import static com.shortlink.common.constant.RedisKeyConstant.SHORT_URI_NUMBER_GENERATOR_KEY;

/**
 * 短链工具类
 *
 * @author wp
 */
@Component
public class ShortUriUtils {
    private static final String HASH_COLLISION_TAG = "collision";
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private RBloomFilterUtils bloomFilterUtils;
    @Resource
    @Lazy
    private ShortLinkService shortLinkService;
    @Resource
    private AdminConfiguration adminConfiguration;

    /**
     * 生成短链
     */
    public String generate(DomainDO domain, String originUrl) {
        if (DomainDO.NUMBER_RULE.equals(domain.getShortUriRule())) {
            return generateByNumber(domain.getDomain());
        } else {
            Integer retryCount = adminConfiguration.getRetryCount();
            String shortUri = null;
            while (ObjectUtil.isEmpty(shortUri)) {
                if (retryCount <= 0) {
                    throw new ServiceException("短链接生成频繁，请稍后重试");
                }
                shortUri = generateByHash(domain.getDomain(), originUrl);
                originUrl = HASH_COLLISION_TAG + originUrl;
                retryCount--;
            }

            return shortUri;
        }
    }

    /**
     * Hash生成短链
     */
    private String generateByHash(String domain, String originUrl) {
        String redisKey = String.format(DOMAIN_SHORT_URI_BLOOM_KEY, domain);
        RBloomFilter<String> shortUriBloomFilter = bloomFilterUtils.create(redisKey);

        String originKey = UserContext.getUsername() + originUrl;
        long hash = MurmurHash.hash64(originKey.getBytes(StandardCharsets.UTF_8));
        String shortUri = Base62Util.encode(hash);
        if (shortUriBloomFilter.contains(shortUri)) {
            ShortLinkDO shortLink = shortLinkService.lambdaQuery()
                    .eq(ShortLinkDO::getDomain, domain)
                    .eq(ShortLinkDO::getShortUri, shortUri)
                    .one();
            if (ObjectUtil.isEmpty(shortLink)) {
                return null;
            }
        }
        return shortUri;
    }

    /**
     * 发号器生成短链
     */
    private String generateByNumber(String domain) {
        // TODO 判断是否存在可重用发号器ID
        String redisKey = String.format(SHORT_URI_NUMBER_GENERATOR_KEY, domain);
        Long id = stringRedisTemplate.opsForValue().increment(redisKey);
        if (id == null) {
            throw new ServiceException("发号器发号失败");
        }
        return Base62Util.encode(id);
    }
}
